/*
	Copyright (C) 2010 Salil Bhagurkar

	This file is part of k3

	k3 is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	k3 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with k3.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <shared.h>

#define KEYS_SZ 200

volatile unsigned char k;

static char keys[KEYS_SZ];
static u32 kptr = 0;


#define KEYB_IRQ 1
int keyb_irq();

static void put_key(char k)
{
	keys[kptr++] = k;
	if(kptr >= KEYS_SZ)
		kptr = 0;
}

static char get_key()
{
	char ret_key;
	u32 i;
	arch_remove_interrupt_handler(KEYB_IRQ);
	ret_key = keys[0];
	for(i=0; i<kptr; i++) {
		keys[i] = keys[i+1];
	}
	kptr--;
	arch_configure_interrupt_handler(KEYB_IRQ, keyb_irq, NULL);
	return ret_key;
}

static void clear_keys()
{
	arch_remove_interrupt_handler(KEYB_IRQ);
	kptr = 0;
	inb(0x60);
	inb(0x60);
	inb(0x60);
	inb(0x60);
	arch_configure_interrupt_handler(KEYB_IRQ, keyb_irq, NULL);
}

static int read(struct fs *referrer, struct fs *this, struct buffer *buf, u32 offset, u32 count)
{
	int err;
	u32 i;
	err = kptr > 0 ? 0 : ENODATA;
	if(!err) {
		for(i=0; i<count; i++) {
			char k = get_key();
			buffer_write_data(buf, &k, i, 1);
		}
	}
	return err;
}


#define KEY_F1 0x80
#define KEY_F2 0x81
#define KEY_F3 0x82
#define KEY_F4 0x83
#define KEY_F5 0x84
#define KEY_F6 0x85
#define KEY_F7 0x86
#define KEY_F8 0x87
#define KEY_F9 0x88
#define KEY_F10 0x89
#define KEY_F11 0x8a
#define KEY_F12 0x8b
#define KEY_UP 0x8c
#define KEY_LEFT 0x8d
#define KEY_DOWN 0x8e
#define KEY_RIGHT 0x8f
#define KEY_HOME 0x90
#define KEY_PGUP 0x91
#define KEY_PGDOWN 0x92
#define KEY_END 0x93
#define KEY_INS 0x94
#define KEY_DEL 0x95

//basic keymap
static char keymap[] =
{
	0,0x1B,'1','2','3','4','5','6',
	'7','8','9','0','-','=','\b','\t',
	'q','w','e','r','t','y','u','i',
	'o','p','[',']','\n',0,'a','s',
	'd','f','g','h','j','k','l',';',
	'\'','`',0,'\\','z','x','c','v',
	'b','n','m',',','.','/',0,0,
	0,' ',0,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,
	KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,0,0,KEY_HOME,
	KEY_UP,KEY_PGUP,'-',KEY_LEFT,'5',KEY_RIGHT,'+',KEY_END,
	KEY_DOWN,KEY_PGDOWN,KEY_INS,KEY_DEL,0,0,0,KEY_F11,
	KEY_F12
};

static char shift_keymap[] =
{
	0,0x1B,'!','@','#','$','%','^',
	'&','*','(',')','_','+','\b','\t',
	'Q','W','E','R','T','Y','U','I',
	'O','P','{','}','\n',0,'A','S',
	'D','F','G','H','J','K','L',':',
	'\"','~',0,'|','Z','X','C','V',
	'B','N','M','<','>','?',0,0,
	0,' ',0,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,
	KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,0,0,KEY_HOME,
	KEY_UP,KEY_PGUP,'-',KEY_LEFT,'5',KEY_RIGHT,'+',KEY_END,
	KEY_DOWN,KEY_PGDOWN,KEY_INS,KEY_DEL,0,0,0,KEY_F11,
	KEY_F12
};


static char shift_status = 0;

/**
Keyboard irq hook
*/
static struct fs *keyb_dev = NULL;
int keyb_irq(void *data)
{
	unsigned char x;
	x = inb(0x60);
	dprint(D_FREQ, "", "x = %x", x);
	if(x == 0x2a || x == 0x36) {
		shift_status = 1;
	} else if(x == 0xaa || x == 0xb6) {
		shift_status = 0;
	} else {
		if(!(x & 0x80)) {
			if(!shift_status) {
				put_key(keymap[x]);
			} else {
				put_key(shift_keymap[x]);
			}
			fsres_update_resource_read(keyb_dev, kptr);
		}
	}
	return 0;
}


static int init_keyb()
{
	arch_configure_interrupt_handler(KEYB_IRQ, keyb_irq, NULL);
	clear_keys();
	return 0;
}

static int deinit_keyb()
{
	arch_remove_interrupt_handler(KEYB_IRQ);
	return 0;
}

static int reboot_write(struct fs *referrer, struct fs *this, struct buffer *buf, u32 offset, u32 count)
{
	inb(0x60);
	inb(0x60);
	while(1) {
		outb(0x64, 0xfe);
	}
}

static int i8042_read(struct fs *referrer, struct fs *this, struct buffer *buf, u32 offset, u32 count)
{
	return 0;
}


/*
static struct fs_system keyb_dev_system = {.flags = SYSTEM_RESOURCE_WAIT_READ, FS_SYSTEM_RESOURCE_INIT, FS_SYSTEM_EVENT_INIT, .read = read, .write = dummy, .rename = dummy};
static struct fs keyb_dev = {.name = "keyb", .flags = FSFLAGS_DEVICE, .system = &keyb_dev_system, DATA_INIT, EXEC_INIT, LINK_INIT};
*/

static int i8042_write(struct fs *referrer, struct fs *this, struct buffer *buf, u32 offset, u32 count)
{
	switch(offset) {
		case DRIVER_PROBE:
			return 0;
		case DRIVER_START:
			init_keyb();
			keyb_dev = fs_create(FSFLAGS_DEVICE, "keyb");
			keyb_dev->system = fs_create_system(SYSTEM_RESOURCE_WAIT_READ, read, dummy, dummy, keyb_dev);
			fs_link(fsparse_get("/devices/class/input"), keyb_dev);
			struct fs *reboot_dev;
			reboot_dev = fs_create(FSFLAGS_DEVICE, "reboot");
			reboot_dev->system = fs_create_system(0, dummy, reboot_write, dummy, reboot_dev);
			fs_link(fsparse_get("/devices/class/power"), reboot_dev);
			return 0;
		case DRIVER_STOP:
			deinit_keyb();
			return 0;
		default:
			return ENOSUP;
	}
	return 0;
}

int register_keyb()
{
	struct fs *i8042_driver = fs_create(FSFLAGS_DEVICE_DRIVER, "i8042");
	i8042_driver->system = fs_create_system(0, i8042_read, i8042_write, dummy, i8042_driver);
	fs_link(fsparse_get("/drivers"), i8042_driver);
	return 0;
}
